package cn.yanjr.common.dao;

import java.lang.reflect.ParameterizedType;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.sql.DataSource;

import org.apache.commons.dbutils.BasicRowProcessor;
import org.apache.commons.dbutils.BeanProcessor;
import org.apache.commons.dbutils.GenerousBeanProcessor;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.RowProcessor;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.MapHandler;
import org.apache.commons.dbutils.handlers.MapListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.log4j.Logger;


/** 
* 调用Apache Commons DBUtil组件的数据库操作类 
* <code> 
* private DbUtilsTemplate dbUtilsTemplate; 
* public void setDbUtilsTemplate(DbUtilsTemplate dbUtilsTemplate) { 
*     this.dbUtilsTemplate = dbUtilsTemplate; 
* } 
* </code> 
* @author Sunshine 
* @version 1.0 2009-07-29 
*/ 
public class BaseDaoImpl<K, T> implements BaseDao<K, T> {
	protected Logger logger= Logger.getLogger(BaseDaoImpl.class);
	protected Class<T> entityClass;
	
	@SuppressWarnings("unchecked")
	public BaseDaoImpl() {
		ParameterizedType genericSuperclass = (ParameterizedType) getClass()
				.getGenericSuperclass();
		this.entityClass = (Class<T>) genericSuperclass.getActualTypeArguments()[1];
	}
	
	protected DataSource dataSource = JDBCUtils.getDataSource();
    protected QueryRunner queryRunner = new QueryRunner(dataSource);;

    public void setDataSource(DataSource dataSource) { 
        this.dataSource = dataSource; 
    }
    
    /* 
	 * 这两个是处理数据库表字段下划线的，
	 * 		与实体类中的属性对应（驼峰规则）
	 */
	BeanProcessor bean = new GenerousBeanProcessor();
	RowProcessor processor = new BasicRowProcessor(bean);
	protected BeanListHandler<T> beanListHandler = new BeanListHandler<T>(entityClass, processor);
	protected BeanHandler<T> beanHandler = new BeanHandler<T>(entityClass, processor);
	

      
    /** 
     * 执行sql语句 
     * @param sql sql语句 
     * @param params 参数数组 
     * @return 受影响的行数 
     */ 
    public int update(String sql, Object... params) { 
        int affectedRows = 0; 
        try { 
        	affectedRows = queryRunner.update(sql, params);
        } catch (SQLException e) { 
        	logger.error("Error occured while attempting to update data", e); 
        } 
        return affectedRows; 
    } 
      
    /** 
     * 执行批量sql语句 
     * @param sql sql语句 
     * @param params 二维参数数组 
     * @return 受影响的行数的数组 
     */ 
    public int[] batchUpdate(String sql, Object[]...params) { 
        int[] affectedRows = new int[0]; 
        try { 
            affectedRows = queryRunner.batch(sql, params); 
        } catch (SQLException e) { 
        	logger.error("Error occured while attempting to batch update data", e); 
        } 
        return affectedRows; 
    }     

    /** 
     * 执行查询，将每行的结果保存到一个Map对象中，然后将所有Map对象保存到List中 
     * @param sql sql语句 
     * @param params 参数数组 
     * @return 查询结果 
     */ 
    public List<Map<String, Object>> find(String sql, Object... params) { 
        List<Map<String, Object>> list = new ArrayList<Map<String,Object>>(); 
        try { 
        	list = queryRunner.query(sql, new MapListHandler(), params); 
        } catch (SQLException e) { 
        	logger.error("Error occured while attempting to query data", e); 
        } 
        return list; 
    } 
      
    /** 
     * 执行查询，将每行的结果保存到Bean中，然后将所有Bean保存到List中 
     * @param entityClass 类名 
     * @param sql sql语句 
     * @return 查询结果 
     */ 
    public List<T> findEntitys(String sql, Object...params) { 
    	 List<T> list = new ArrayList<>(); 
         try { 
         	list = queryRunner.query(sql, new BeanListHandler<T>(entityClass, processor), params); 
         } catch (SQLException e) { 
         	logger.error("Error occured while attempting to query data", e); 
         } 
         return list; 
    } 
      
    /** 
     * 查询出结果集中的第一条记录，并封装成对象 
     * @param entityClass 类名 
     * @param sql sql语句 
     * @param params 参数数组 
     * @return 对象 
     */ 
    @SuppressWarnings("unchecked") 
    public T findFirstEntity(String sql, Object...params) { 
//        queryRunner = new QueryRunner(dataSource); 
        Object object = null; 
        try { 
        	object = queryRunner.query(sql, new BeanHandler<T>(entityClass, processor), params);
        } catch (SQLException e) { 
        	logger.error("Error occured while attempting to query data", e); 
        } 
        return (T) object; 
    } 
      
    /** 
     * 查询出结果集中的第一条记录，并封装成Map对象 
     * @param sql sql语句 
     * @param params 参数数组 
     * @return 封装为Map的对象 
     */ 
    public Map<String, Object> findFirst(String sql, Object...params) { 
//        queryRunner = new QueryRunner(dataSource); 
        Map<String, Object> map = null; 
        try { 
        	map = (Map<String, Object>) queryRunner.query(sql, new MapHandler(), params); 
        } catch (SQLException e) { 
        	logger.error("Error occured while attempting to query data", e); 
        } 
        return map; 
    } 
      
    /** 
     * 查询某一条记录，并将指定列的数据转换为Object 
     * @param sql sql语句 
     * @param columnName 列名 
     * @param params 参数数组 
     * @return 结果对象 
     */ 
	public Object findBy(String sql, String columnName, Object...params) { 
//        queryRunner = new QueryRunner(dataSource); 
        Object object = null; 
        try { 
        	object = queryRunner.query(sql, new ScalarHandler<Object>(columnName), params); 
        } catch (SQLException e) { 
        	logger.error("Error occured while attempting to query data", e); 
        } 
        return object; 
    } 
      
      
    /** 
     * 查询某一条记录，并将指定列的数据转换为Object 
     * @param sql sql语句 
     * @param columnIndex 列索引 
     * @param params 参数数组 
     * @return 结果对象 
     */ 
    public Object findBy(String sql, int columnIndex, Object...params) { 
//        queryRunner = new QueryRunner(dataSource); 
        Object object = null; 
        try { 
        	object = queryRunner.query(sql, new ScalarHandler<Object>(columnIndex), params); 
        } catch (SQLException e) { 
        	logger.error("Error occured while attempting to query data", e);
        } 
        return object; 
    }

	@Override
	public int count(String sql, Object... params) {
		StringBuilder sqlstr = new StringBuilder();
		sqlstr
		.append("select count(*) from ( ")
		.append(sql)
		.append(")");
		Long count = null;
		try {
			count = queryRunner.query(sqlstr.toString(), new ScalarHandler<Long>());
		} catch (SQLException e) {
			logger.error("Error occured while attempting to query data", e);
		}
		
		return count != null ? count.intValue() : 0;
	}

	@Override
	public Map<String, Object> findPage(String sql, int page, int limit, Object...params) {
		Map<String, Object> result = new HashMap<>();
		if(params != null && params.length > 0) {
			params = ArrayUtils.addAll(params, new Object[] {(page - 1) * limit, limit});
		} else {
			params = new Object[] {(page - 1) * limit, limit};
		}
		List<Map<String, Object>> rows = find(sql, params);
		int total = count(sql, params);
		result.put("rows", rows);
		result.put("total", total);
		result.put("totalPage", total == 0 ? 1 : Math.ceil((double) total / (double)limit));
		result.put("page", page);
		result.put("pageSize", limit);
		return result;
	} 
	@Override
	public Map<String, Object> findEntityPage(String sql, int page, int limit, Object...params) {
		Map<String, Object> result = new HashMap<>();
		if(params != null && params.length > 0) {
			params = ArrayUtils.addAll(params, new Object[] {(page - 1) * limit, limit});
		} else {
			params = new Object[] {(page - 1) * limit, limit};
		}
		List<T> rows = findEntitys(sql, params);
		int total = count(sql, params);
		result.put("rows", rows);
		result.put("total", total);
		result.put("totalPage", total == 0 ? 1 : Math.ceil((double) total / (double)limit));
		result.put("page", page);
		result.put("pageSize", limit);
		return result;
	} 
}
